const STATUS = {
  PENDING: "PENDING",
  FUFILLED: "FUFILLED",
  REJECTED: "REJECTED",
};

/**
 * 
 * @param {*} x then回调的返回值
 * @param {*} promise promise2的实例
 * @param {*} resolve promise成功
 * @param {*} reject promise失败
 */

function resolvePromise ( x, promise2, resolve, reject ) {
  if ( promise2 === x ) {
    return reject( new TypeError( "循环引用,造成无限死循环" ) );
  }
  // 对象或者function都有可能是Promise
  if ( (typeof x === 'object' && x !== null) || typeof x ==='function') {
     // 防止别人实现的promise即可以成功，也可以失败
     let called; 
    try {
      const then = x.then
      if(typeof then ==='function'){
        // 执行promise.then
        then.call(x,(y)=>{
          if (called) return;
          called = true;
          // y有可能还是一个promise，递归到普通值结束
          resolvePromise(y, promise2, resolve, reject);
        },(r)=>{
          if (called) return;
          called = true;
          // promise失败直接到下一个失败的then回调
          reject(r)
        })
      }else{
        if (called) return;
        called = true;
         // 普通值走下一个成功的then回调
        resolve(x)
      }
      
    } catch (e) {
      if (called) return;
      called = true;
       // 取then错误走下一个失败的then回调
      reject(e)
    }

  } else {
    // 普通值走下一个成功的then回调
    resolve( x )
  }

}

class Promise {
  constructor ( executor ) {
    this.status = STATUS.PENDING
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallbacks = []
    this.onRejectedCallbacks = []
    const resolve = ( value ) => {
      if ( this.status === STATUS.PENDING ) {
        this.status = STATUS.FUFILLED
        this.value = value
        this.onFulfilledCallbacks.forEach( onRejected => {
          onRejected( value )
        } )
      }

    }
    const reject = ( reason ) => {
      if ( this.status === STATUS.PENDING ) {
        this.status = STATUS.REJECTED
        this.reason = reason
        this.onRejectedCallbacks.forEach( onRejected => {
          onRejected( reason )
        } )
      }


    }
    try {
      executor( resolve, reject )
    } catch ( e ) {
      reject( e )
    }

  }
  
  then ( onFulfilled, onRejected ) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : ( value ) => value
    onRejected = typeof onRejected === 'function' ? onRejected : ( err ) => { throw err };

    const promise2 = new Promise( ( resolve, reject ) => {
      if ( this.status === STATUS.PENDING ) {
        this.onFulfilledCallbacks.push( () => {
          // 保证promise2实例化完成
          setTimeout( () => {
            try {
              const x = onFulfilled( this.value )
              // 处理回调的结果
              resolvePromise( x, promise2, resolve, reject )
            } catch ( e ) {
              // then回调出新异常直接调用下一个失败的then
              reject( e )
            }

          } )

        } )
        this.onRejectedCallbacks.push( () => {
          setTimeout( () => {
            try {
              const x = onRejected( this.reason )
              resolvePromise( x, promise2, resolve, reject )
            } catch ( e ) {
              reject( e )
            }

          } )

        } )
      } else if ( this.status === STATUS.FUFILLED ) {
        setTimeout( () => {
          try {
            const x = onFulfilled( this.value )
            resolvePromise( x, promise2, resolve, reject )
          } catch ( e ) {
            reject( e )
          }

        } )

      } else {
        setTimeout( () => {
          try {
            const x = onRejected( this.reason )
            resolvePromise( x, promise2, resolve, reject )
          } catch ( e ) {
            reject( e )
          }
        } )

      }
    } )
    return promise2

  }

  catch(onRejected) {
    return this.then(null, onRejected);
  }

  static resolve(val) {
    return new Promise((resolve) => {
      resolve(val);
    });
  }

  static reject(reason) {
    return new Promise((resolve, reject) => {
      reject(reason);
    });
  }

  static all(promisArr) {
    return new Promise((resolve, reject) => {
      let result = [],
        count = 0;

      function done(i, data) {
        result[i] = data;

        if (count++ == i) {
          resolve(result);
        }
      }
      promisArr.map((p, i) => {
        p.then((data) => done(i, data), reject);
      });
    });
  }

  static race(promisArr) {
    return new Promise((resolve, reject) => {
      promisArr.map((p) => {
        p.then((data) => resolve(data), reject);
      });
    });
  }
}



Promise.defer = Promise.deferred = function () {
  let dfd = {};
  dfd.promise = new Promise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject;
  });
  return dfd;
};
module.exports = Promise;